昨天我們看到了 Laravel 如何引入 routes/web.php
等等檔案
今天我們來看看 Laravel 怎麼建立路徑。
一般來說,Laravel 框架宣告路徑有兩種方式
第一種是比較正式的宣告,使用 Controller 的名字和方法名稱
use App\Http\Controllers\UserController;
Route::get('/user', [UserController::class, 'index']);
這邊的 Route
全名為 Illuminate\Support\Facades\Route
class Route extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'router';
}
}
如果只看這段程式,是無法往下繼續找實作的
必須要找註解內標記的
@see \Illuminate\Routing\Router
才能找到這段程式實際實作的位置。
Router
裡面實作 get()
的部分如下
/**
* Register a new GET route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function get($uri, $action = null)
{
return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}
如果我們定義了 GET 的路徑,Laravel 會幫我們加上一個 HEAD 的路徑。
根據 MDN Web Docs 的說明, HEAD 的用途如下
HTTP HEAD 方法請求返回與使用 HTTP GET 方法請求相同 URL 時將返回的標頭。例如,如果某個 URL 可能產生大文件下載,則 HEAD 請求可以讀取其 Content-Length 標頭以檢查文件大小,而無需實際下載文件。
這邊我們先不處理這一段,往下看 GET 建立的內容。
addRoute()
實作的內容如下
public function addRoute($methods, $uri, $action)
{
return $this->routes->add($this->createRoute($methods, $uri, $action));
}
這邊的邏輯很簡單,就是透過 createRoute()
建立路由物件之後,將建立好的物件加入 $this->routes
裡面
createRoute()
實作如下:
protected function createRoute($methods, $uri, $action)
{
// If the route is routing to a controller we will parse the route action into
// an acceptable array format before registering it and creating this route
// instance itself. We need to build the Closure that will call this out.
if ($this->actionReferencesController($action)) {
$action = $this->convertToControllerAction($action);
}
$route = $this->newRoute(
$methods, $this->prefix($uri), $action
);
// If we have groups that need to be merged, we will merge them now after this
// route has already been created and is ready to go. After we're done with
// the merge we will be ready to return the route back out to the caller.
if ($this->hasGroupStack()) {
$this->mergeGroupAttributesIntoRoute($route);
}
$this->addWhereClausesToRoute($route);
return $route;
}
又是一個註解比起實作內容要多的程式。相信各位讀者看到這邊應該已經有感覺,當遇到這種狀況時,就代表我們開始接觸到一些需要說明的核心邏輯,而且這些邏輯通常複雜到我們無法快速的透過程式碼理解其內容。
我們來看看第一段邏輯
// If the route is routing to a controller we will parse the route action into
// an acceptable array format before registering it and creating this route
// instance itself. We need to build the Closure that will call this out.
if ($this->actionReferencesController($action)) {
$action = $this->convertToControllerAction($action);
}
這段會將前面輸入的內容,轉換成可用的陣列。
經過轉換之後,接著會建立一個新的 Route
物件
/**
* Create a new Route object.
*
* @param array|string $methods
* @param string $uri
* @param mixed $action
* @return \Illuminate\Routing\Route
*/
public function newRoute($methods, $uri, $action)
{
return (new Route($methods, $uri, $action))
->setRouter($this)
->setContainer($this->container);
}
建立好了之後,就可以將新建立好的 Route
物件透過 $this->routes->add()
加入 $this->routes
內。
這邊的 $this->routes
實作了 \Illuminate\Routing\RouteCollectionInterface
,當我們查看誰實作這個介面時,可以發現只有 AbstractRouteCollection
實作該介面
abstract class AbstractRouteCollection implements Countable, IteratorAggregate, RouteCollectionInterface
至於繼承 AbstractRouteCollection
的,則是 Illuminate\Routing\RouteCollection
類別
class RouteCollection extends AbstractRouteCollection
到這邊,我們看了當我們撰寫
Route::get('/user', [UserController::class, 'index']);
這段程式時, Laravel 是怎麼處理這段內容,建立一個 Route
物件,並將其加入到 RouteCollection
裡面。